package main

import (
	"fmt"
	"github.com/fogleman/gg"
	"math/rand"
	"strconv"
)

func main() {
	conf := &gridConf{
		Margin:         20,
		MarginTop:      55,
		Padding:        10,
		FontPointTitle: 22,
		FontPointDate:  16,
		OutLineWidth:   2,
		InLineWidth:    1,
		FontPath:       "./ttf/FangZhengKaiTi.ttf",
		Rows:           6,
		RowHight:       50,
		Columns:        10,
		ColumnWidth:    110,
	}
	title := "数据核对表"
	mark := "时间：2022-08-17 00:00:00"
	dataTitle := []titleParse{
		titleParse{
			Field: "Name",
			Name:  "名字",
		},
		titleParse{
			Field: "Sex",
			Name:  "性别",
		},
		titleParse{
			Field: "Score",
			Name:  "分数",
		},
		titleParse{
			Field: "Xuexiao",
			Name:  "学校",
		},
		titleParse{
			Field: "Addr",
			Name:  "地址",
		},
		titleParse{
			Field: "Level",
			Name:  "等级",
		},
		titleParse{
			Field: "Mark",
			Name:  "备注1",
		},
		titleParse{
			Field: "Mark2",
			Name:  "备注2",
		},
		titleParse{
			Field: "Mark3",
			Name:  "备注3",
		},
		titleParse{
			Field: "Mark4",
			Name:  "备注4",
		},
	}
	data := []map[string]interface{}{}
	for i := 1; i <= 5; i++ {
		str := strconv.Itoa(i)
		score := rand.Float64()
		item := map[string]interface{}{
			"Name":    "名字" + str,
			"Sex":     "性别" + str,
			"Score":   score,
			"Xuexiao": "某学" + str,
			"Addr":    "无路" + str,
			"Level":   i + 1,
			"Mark":    "加" + str,
			"Mark2":   "符2" + str,
			"Mark3":   "加3" + str,
			"Mark4":   "让4" + str,
		}
		data = append(data, item)
	}

	testGg(conf, title, mark, dataTitle, data)
}

type gridConf struct {
	Margin         float64 //距离四周的边界值
	MarginTop      float64 //距离顶部的边界值
	Padding        int     //每个单元格的内边距
	FontPointTitle float64 //标题字体占用的像素点
	FontPointDate  float64 //日期字体占用的像素点
	OutLineWidth   float64 //外边框线条宽度
	InLineWidth    float64 //内边框线条宽度
	FontPath       string  //字体路径
	Rows           int     //多少行
	RowHight       float64 //行高
	Columns        int     //多少列
	ColumnWidth    float64 //列宽
}

type titleParse struct {
	Field string
	Name  string
}

func testGg(conf *gridConf, title, mark string, dataTitle []titleParse, data []map[string]interface{}) {
	//上下文，含长和宽
	var w = int(conf.Margin*2) + conf.Columns*int(conf.ColumnWidth)
	var h = int(conf.MarginTop+conf.Margin) + conf.Rows*int(conf.RowHight)
	dc := gg.NewContext(w, h)
	//设置当前图片底色
	dc.SetRGB(1, 1, 0.745)
	dc.Clear() //清理一下上下文，下面开始画画

	//设置图片标题及日期显示
	dc.SetRGB(0, 0, 0)                                                          //设置标题文字颜色
	if err := dc.LoadFontFace(conf.FontPath, conf.FontPointTitle); err != nil { //加载字体及字体大小
		panic(err)
	}
	tWidth, _ := dc.MeasureString(title) // 测算字符串将在画布中占用的宽与长
	dc.DrawStringWrapped(title, 0, 0, -float64(w/8)*0.033, -1, tWidth, 1, gg.AlignCenter)
	if err := dc.LoadFontFace(conf.FontPath, conf.FontPointDate); err != nil {
		panic(err)
	}
	dateY := conf.MarginTop - 5
	dc.DrawString(mark, conf.Margin, dateY)

	//设置线条颜色及透明度
	dc.SetRGBA(0, 0, 0, 1)
	//先画行线条
	RowOverX := conf.Margin + float64(conf.Columns)*conf.ColumnWidth
	ColumnOverY := conf.MarginTop + float64(conf.Rows)*conf.RowHight

	//画列的线条
	for i := 0; i <= conf.Columns; i++ {
		if i == 0 || i == conf.Columns { //第一条和最后一条线加粗
			dc.SetLineWidth(conf.OutLineWidth) //设置外行线条宽度
		} else {
			dc.SetLineWidth(conf.InLineWidth) //设置内行线条宽度
		}
		RowX := float64(i) * conf.ColumnWidth
		dc.DrawLine(conf.Margin+RowX, conf.MarginTop, conf.Margin+RowX, ColumnOverY) //从左上角向右画
		//没有这句是不会把线最终画出来的
		dc.Stroke()
	}
	//画行的线条
	for i := 0; i <= conf.Rows; i++ {
		if i == 0 || i == conf.Rows { //第一条和最后一条线加粗
			dc.SetLineWidth(conf.OutLineWidth) //设置外行线条宽度
		} else {
			dc.SetLineWidth(conf.InLineWidth) //设置内行线条宽度
		}
		ColumnY := float64(i) * conf.RowHight
		dc.DrawLine(conf.Margin, conf.MarginTop+ColumnY, RowOverX, conf.MarginTop+ColumnY) //从左上角的位置开始向下画
		//没有这句是不会把线最终画出来的000
		dc.Stroke()
		//写字
		childNum := len(data)
		if i <= childNum { //这里需要画边界线，所有就会多出一个边界值
			index := 0 //写字的遍历个数
			for _, n := range dataTitle {
				if index >= conf.Columns {
					break
				}
				fontX := conf.Margin + float64(index)*conf.ColumnWidth
				fontY := conf.MarginTop + conf.RowHight*float64(i)
				ay := -float64(conf.RowHight/8) * 0.25
				if i == 0 { //第一行写标题
					sWidth, _ := dc.MeasureString(n.Name) // 测算字符串将在画布中占用的宽与长
					ax := -float64(conf.ColumnWidth/8) * 0.08
					dc.DrawStringWrapped(n.Name, fontX, fontY, ax, ay, sWidth, 1, gg.AlignCenter)
				} else { //后面的写入具体的数据
					itemData := data[i-1]
					txt := itemData[n.Field]
					setVal := fmt.Sprintf("%v", txt)
					sWidth, _ := dc.MeasureString(setVal) // 测算字符串将在画布中占用的宽与长
					if sWidth > conf.ColumnWidth {
						//1个像素=1bite,1个字节=1B,因为1b=8B,所以1个像素=8字节
						cutLen := int(conf.ColumnWidth/8) - (conf.Padding*2)/8
						setVal = setVal[0:cutLen]
						setVal = setVal + "..."
						sWidth, _ = dc.MeasureString(setVal)
					}
					sWidth = sWidth + float64(conf.Padding)
					dc.DrawStringWrapped(setVal, fontX, fontY, 0, ay, sWidth, 1, gg.AlignCenter)
				}
				index++
			}
		}
	}

	dc.SavePNG("./images/test.png") //保存上下文为一张图片
}
